package hn.sigit.logic.viewer;

import hn.sigit.dao.hnd.cadastre.HND_LayerDAO;
import hn.sigit.logic.general.GeneralHelper;
import hn.sigit.model.commons.ISpatialZone;
import hn.sigit.model.hnd.cadastre.HND_Layer;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringEscapeUtils;
import org.deegree.geometry.Envelope;
import org.deegree.protocol.wms.client.WMSClient111;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

@Name("mapHelper")
@Scope(value = ScopeType.CONVERSATION)
@AutoCreate
public class MapHelper implements Serializable {
	private static final long serialVersionUID = 1L;
	
	public static String NAME = "mapHelper";

	private String mapOptions;
	private String mapScript;
	private String dataEntryMapScript;
	private List<HND_Layer> layerList;
	private boolean parcelLayerOnly;
	
	private boolean showEditControls;
	private boolean highlightParcelList;
	
	private List<? extends ISpatialZone> features;
	private List<? extends ISpatialZone> readOnlyFeatures;
	
	private String selectedLayerFQName;
	

	@In(create=true)
	private GeneralHelper generalHelper;

	
	public MapHelper() {
		this.parcelLayerOnly = false;
		this.showEditControls = true;
		this.highlightParcelList = true;
	}

	public String mapOptions(boolean parcelLayerOnly) {
		if (this.mapOptions == null || this.parcelLayerOnly != parcelLayerOnly) {
			this.parcelLayerOnly = parcelLayerOnly;
			try {
				WMSClient111 wmsClient = new WMSClient111(new URL(generalHelper.getWmsCapabilitiesUrl111()));
				
				List<String> namedLayers = wmsClient.getNamedLayers();
				List<String> boundsInclLayers = new ArrayList<String>();
				
				if (namedLayers == null || namedLayers.size() == 0)
					return null;

				String layerName;
				for (HND_Layer layer : getLayerList()) {
					if (!parcelLayerOnly || layer.isParcelLayer()) {
						layerName = layer.getFullQualifiedName();
						if (namedLayers.contains(layerName))
							boundsInclLayers.add(layerName);
					}
				}
				
				Envelope env = wmsClient.getBoundingBox(generalHelper.getWorkingSRS(), boundsInclLayers);

				this.mapOptions = String.format("{ controls:[], " +
						"maxExtent: new OpenLayers.Bounds(%f, %f, %f, %f), " +
						//"minResolution: 0.1, " +
						"maxResolution: 75, " +
						//"projection: new OpenLayers.Projection('EPSG:32616'), " +
						//"displayProjection: new OpenLayers.Projection('EPSG:32616'), " +
						"units: 'm' }",
						
						env.getMin().get0(),
						env.getMin().get1(),
						env.getMax().get0(),
						env.getMax().get1());
			}
			catch (MalformedURLException e) {
				e.printStackTrace();
			}
		}
		return this.mapOptions;
	}
	
	public String getDataEntryMapScript() {
		if (dataEntryMapScript != null) return dataEntryMapScript;
		
		StringBuilder sb = new StringBuilder(
				//"var click = new OpenLayers.Control.Click();" +
				//"mapEditor.addControl(click);" +
				//"click.activate();" +
				"var vector_style = new OpenLayers.Style({'strokeColor': '#ff0000','strokeWidth': 3});" +
				"var vector_style_map = new OpenLayers.StyleMap({'default': vector_style});" +
				"vlayer.styleMap = vector_style_map;" +
				"var select_feature_control = new OpenLayers.Control.SelectFeature(parcelFeaturesLayer,{multiple: false,toggle: true,multipleKey: 'shiftKey'});" +
				"mapEditor.addControl(select_feature_control);" +
				"select_feature_control.id = 'parcelSelectControl';"
		);
		
		addLayers(sb, false);

		addLayerStyles(sb);
		
		
		addInitialFeatures(sb);
		
		addSnapCapability(sb);

		sb.append(
				"var vertexStyle = {" +
				"	cursor: 'pointer'," +
				"	fillColor: '#787878'," +
				"	fillOpacity: .8," +
				"	fontColor: '#343434'," +
			    "	pointRadius: 14," +
				"	strokeColor: '#232323'," +
				"	strokeDashstyle: 'dot'," +
			    "	strokeWidth: 3" +
			    "};" +
			    "polyLayer.style = vertexStyle;" +
			    "mapEditor.setLayerIndex(polyLayer, 99);" +

			    "mapEditor.setLayerIndex(vlayer, 98);" +
			    "doZoom();"
		);

		return dataEntryMapScript = sb.toString();
	}


	public String getMapScript() {
		if (mapScript != null) return mapScript;
		
		StringBuilder sb = new StringBuilder(
				"var select_feature_control = new OpenLayers.Control.SelectFeature(parcelFeaturesLayer,{multiple: false,toggle: true,multipleKey: 'shiftKey'});" +
				"mapEditor.addControl(select_feature_control);" +
				"select_feature_control.id = 'parcelSelectControl';"
		);

		addLayers(sb, true);
		addSimpleLayerStyles(sb);
		addRasterLayer(sb);
		addSnapCapability(sb);
		
		addInitialFeatures(sb);
		
		return mapScript = sb.toString();
	}
	
	
	private void addLayers(StringBuilder sb, boolean includeInfoControl) {
		for (HND_Layer layer : getLayerList()) {
			if (!parcelLayerOnly || layer.isParcelLayer())
				sb.append(generalHelper.drawLayerJS("mapEditor", layer));

			if (includeInfoControl && layer.getFullQualifiedName().equals(getSelectedLayerFQName())) {
				sb.append(String.format(
					"var infoControl = new OpenLayers.Control.WMSGetFeatureInfo({" +
					"	url: '%s'," +
					"	title: 'test'," +
					"	layers: [wms]," +
					"	infoFormat: 'application/vnd.ogc.gml'," +
					"	queryVisible: true," +
					"	allowSelection: true," +
					"});",

					generalHelper.getWmsUrl()
				));

				if (highlightParcelList) {
					sb.append(
						"infoControl.events.register('getfeatureinfo', this, updateHighlightedParcelList);" +
						"infoControl.events.register('nogetfeatureinfo', this, unHighlightAllParcels);"
					);
				}

				sb.append(
					"infoControl.id = 'infoControl';" +				
					"mapEditor.addControl(infoControl);" +
					"infoControl.activate();"
				);
			}
		}
	}
	
	private void addSimpleLayerStyles(StringBuilder sb) {
		sb.append(
				"var vector_style = new OpenLayers.Style({'strokeColor': '#ff0000','strokeWidth': 3});" +
				"var vector_style_map = new OpenLayers.StyleMap({'default': vector_style});" +
				"vlayer.styleMap = vector_style_map;" +
				
				"var parcel_style = new OpenLayers.Style({" +
				"	'fillColor': '#669933'," +
				"	'fillOpacity': .8," +
				"	'graphicName': 'square'," +
				"	'label': '${noficha}'," +
				"	'pointRadius': 16," +
				"	'strokeColor': '#aaee77'," +
				"	'strokeDashstyle': 'solid'," +
				"	'strokeWidth': 4" +
				"});" +
				"" +
				"" +
				
				"var parcel_style_select = new OpenLayers.Style({" +
				"	'fillColor': '#FF0000'," +
				"	'fillOpacity': .8," +
				"	'graphicName': 'square'," +
				"	'label': '${noficha}'," +
				"	'pointRadius': 16," +
				"	'strokeColor': '#343434'," +
				"	'strokeDashstyle': 'solid'," +
				"	'strokeWidth': 4" +
				"});" +
				"var parcel_style_map = new OpenLayers.StyleMap({'default': parcel_style, 'select': parcel_style_select});" +
				"var readonly_parcel_style_map = new OpenLayers.StyleMap({'default': parcel_style});" +
				
				"parcelFeaturesLayer.styleMap = parcel_style_map;" +
				"neighborParcelLayer.styleMap = readonly_parcel_style_map;"
		);
	}
	
	private void addLayerStyles(StringBuilder sb) {
		sb.append(
				"var vector_style = new OpenLayers.Style({'strokeColor': '#ff0000','strokeWidth': 3});" +
				"var vector_style_map = new OpenLayers.StyleMap({'default': vector_style});" +
				"vlayer.styleMap = vector_style_map;" +
				
				"var parcel_style = new OpenLayers.Style({" +
				"	'fillColor': '#669933'," +
				"	'fillOpacity': .8," +
				"	'graphicName': 'square'," +
				"	'label': '${label}'," +
				"	'pointRadius': 16," +
				"	'strokeColor': '#aaee77'," +
				"	'strokeDashstyle': 'solid'," +
				"	'strokeWidth': 4" +
				"});" +
				"" +
				"" +
				
				"var parcel_style_select = new OpenLayers.Style({" +
				"	'fillColor': '#3BB9FF'," +
				"	'fillOpacity': .8," +
				"	'graphicName': 'square'," +
				"	'label': '${label}'," +
				"	'pointRadius': 16," +
				"	'strokeColor': '#343434'," +
				"	'strokeDashstyle': 'solid'," +
				"	'strokeWidth': 4" +
				"});" +
				"var parcel_style_map = new OpenLayers.StyleMap({'default': parcel_style, 'select': parcel_style_select});" +
				"var readonly_parcel_style_map = new OpenLayers.StyleMap({'default': parcel_style});" +
				
				"var parcel_symbolizer_lookup = {" +
				"	'0': {" +
				"		'fillColor': '#669933', 'fillOpacity': .5, 'strokeColor': '#aaee77', 'strokeWidth': 3, 'pointRadius': 8, 'label': '${label}'" +
				"	}," +
				"	'1': {" +
				"		'fillColor': '#FF0000', 'fillOpacity': .5, 'strokeColor': '#aaee77', 'strokeWidth': 3, 'pointRadius': 8, 'label': '${label}'" +
				"	}" +
				"};" +
				
				"parcel_style_map.addUniqueValueRules('default', 'neighborLevel', parcel_symbolizer_lookup);" +
				"parcelFeaturesLayer.styleMap = parcel_style_map;" +
				"neighborParcelLayer.styleMap = readonly_parcel_style_map;"
		);
	}
	
	private void addRasterLayer(StringBuilder sb) {
		//TODO: Parametrizar esto!
		try {
			String rasterLayerName = generalHelper.getRasterLayerName();
			WMSClient111 wmsClient = new WMSClient111(new URL(generalHelper.getWmsCapabilitiesUrl111()));
	
			if (wmsClient.getNamedLayers().contains(rasterLayerName)) {
				sb.append(String.format(
						"var ortho = new OpenLayers.Layer.WMS(" +
						"	'%s'," +
						"	'%s'," +
						"	{layers: '%s', transparent: true, format: 'image/png'}," +
						"	{projection: '%s'});" +
						"ortho.id = '%s';" +
						"ortho.setVisibility(false);" +
						"mapEditor.addLayer(ortho);",
						
						rasterLayerName,
						generalHelper.getWmsUrl(),
						rasterLayerName,
						generalHelper.getWorkingSRS(),
						rasterLayerName
				));
			}
		}
		catch (MalformedURLException e) {
			e.printStackTrace();
		}		
	}
	
	private void addInitialFeatures(StringBuilder sb) {
		if (features != null || readOnlyFeatures != null) {
			String allSZGml;
			sb.append("var allSZGml, spatialZoneWKT, geom;");
			
			if (features != null) {
				allSZGml = generalHelper.getSpatialZonesGML(features, 0);
				sb.append(mapScriptUtil(allSZGml, 0));
			}

			if (readOnlyFeatures != null) {
				allSZGml = generalHelper.getSpatialZonesGML(readOnlyFeatures, 1);
				sb.append(mapScriptUtil(allSZGml, 1));
			}
		}
	}
	private String mapScriptUtil(String gml, int neighborLevel) {
		return String.format("allSZGml = '%s';" +
				"geom = new OpenLayers.Format.GML().read(allSZGml);" +
				(neighborLevel == 0 ? "parcelFeaturesLayer" : "neighborParcelLayer") +
				".addFeatures(geom);",
				
				StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeJavaScript(gml)));
	}

	private void addSnapCapability(StringBuilder sb) {
		sb.append(
				"snap = new OpenLayers.Control.Snapping({" +
                "	layer: polyLayer," +
                "	targets: [polyLayer, parcelFeaturesLayer]," +
                "	greedy: false" +
                "});" +
                "snap.activate();"
                
                /*
                "var panel = new OpenLayers.Control.Panel({" +
                "	displayClass: 'olControlEditingToolbar'" +
                "});" +
                */
                /*
                "draw = new OpenLayers.Control.DrawFeature(" +
                "	polyLayer, OpenLayers.Handler.Polygon," +
                "	{displayClass: 'olControlDrawFeaturePoint', title: '" + generalHelper.getResBundle().loadMessage("map.draw_feature") + "', handlerOptions: {holeModifier: 'altKey'}}" +
            	");" +
            	"modify = new OpenLayers.Control.ModifyFeature(" +
                "	polyLayer, {displayClass: 'olControlModifyFeature', title: '" + generalHelper.getResBundle().loadMessage("map.modify_feature") + "'}" +
                ");"
                */            
		);
	}


	private List<HND_Layer> getLayerList() {
		if (layerList == null) {
			layerList = HND_LayerDAO.loadLayers(true);
		}
		return layerList;
	}

	public boolean isShowEditControls() {
		return showEditControls;
	}
	public void setShowEditControls(boolean showEditControls) {
		this.showEditControls = showEditControls;
	}

	public boolean isHighlightParcelList() {
		return highlightParcelList;
	}
	public void setHighlightParcelList(boolean highlightParcelList) {
		this.highlightParcelList = highlightParcelList;
	}

	public boolean isParcelLayerOnly() {
		return parcelLayerOnly;
	}
	public void setParcelLayerOnly(boolean parcelLayerOnly) {
		this.parcelLayerOnly = parcelLayerOnly;
	}
	
	
	public List<? extends ISpatialZone> getFeatures() {
		return features;
	}
	public void setFeatures(List<? extends ISpatialZone> features) {
		this.features = features;
	}

	public List<? extends ISpatialZone> getReadOnlyFeatures() {
		return readOnlyFeatures;
	}
	public void setReadOnlyFeatures(List<? extends ISpatialZone> readOnlyFeatures) {
		this.readOnlyFeatures = readOnlyFeatures;
	}


	public String getSelectedLayerFQName() {
		if (selectedLayerFQName == null) {
			selectedLayerFQName = "";
		}
		return selectedLayerFQName;
	}
	public void setSelectedLayerFQName(String selectedLayerFQName) {
		this.selectedLayerFQName = selectedLayerFQName;
	}
}
